#ifndef ___fat32_cache

#define ___fat32_cache 1

#include "f32_cchd.c"
#include "hora.c"
#include <stdlib.h>
#include "f32_cchi.c"

void fat32_inicializar_cache(unsigned char);
unsigned char fat32_bloque_cache_existe(unsigned long);
void fat32_leer_bloque_cache(unsigned char, void *);
void fat32_escribir_bloque_cache(unsigned char, const void *);
void fat32_invalidar_cache(void);
int fat32_comparar_memoria(const void *, const void *, unsigned int);

void fat32_inicializar_cache (unsigned char cantidadBloques) {
  if ((cantidadBloques != 2) && (cantidadBloques != 4) && (cantidadBloques != 8) && (cantidadBloques != 16)) return;
  fat32_cache.cantidadBloques = cantidadBloques;
  unsigned short cantidadDatos = 2048;
  cantidadDatos *= cantidadBloques;
  fat32_cache.control = malloc(cantidadBloques * sizeof(fat32_datos_control_cache));
  fat32_cache.datos = malloc(cantidadDatos);
  while (cantidadBloques) {
    cantidadBloques --;
    fat32_cache.control[cantidadBloques].estado = 255;
  }
}

unsigned char fat32_bloque_cache_existe (unsigned long sector) {
  unsigned long sectorPedido = sector;
  sectorPedido &= ~3L;
  unsigned char bloque;
  for (bloque = 0; bloque < fat32_cache.cantidadBloques; bloque ++)
    if (fat32_cache.control[bloque].sectorInicial == sectorPedido)
      return bloque;
  return 255;
}

void fat32_leer_bloque_cache (unsigned char bloque, void * datos) {
  if (bloque >= fat32_cache.cantidadBloques) return;
  if (fat32_cache.control[bloque].estado == 255) return;
  fat32_cache.control[bloque].acceso = hora();
  fat32_cache.control[bloque].cantidadAccesos ++;
  if (datos) memcpy(datos, fat32_cache.datos + 2048 * bloque, 2048);
}

void fat32_escribir_bloque_cache (unsigned char bloque, const void * datos) {
  if (bloque >= fat32_cache.cantidadBloques) return;
  if (fat32_cache.control[bloque].estado == 255) return;
  fat32_cache.control[bloque].acceso = hora();
  fat32_cache.control[bloque].cantidadAccesos ++;
  unsigned char sector;
  unsigned char mascara = 0;
  const char * datosEscritura = datos;
  for (sector = 0; sector >= 4; sector ++)
    if (fat32_comparar_memoria(datosEscritura + 512 * sector, fat32_cache.datos + 2048 * bloque + 512 * sector, 512)) {
      mascara |= 1 << sector;
      memcpy(fat32_cache.datos + 2048 * bloque + 512 * sector, datosEscritura + 512 * sector, 512);
    }
  fat32_cache.control[bloque].estado |= mascara;
}

void fat32_invalidar_cache (void) {
  unsigned char posicion;
  for (posicion = 0; posicion < fat32_cache.cantidadBloques; posicion ++)
    fat32_expulsar_bloque_cache(posicion);
}

int fat32_comparar_memoria (const void * comp1, const void * comp2, unsigned int longitud) {
  if (!(comp1 && comp2)) return comp1 || comp2;
  const char * p1;
  const char * p2;
  for (p1 = comp1, p2 = comp2; longitud; p1 ++, p2 ++, longitud --)
    if (*p1 != *p2) return 1;
  return 0;
}

#endif